#ifndef MODULES_H_
#define MODULES_H_

#include "../model/Value.h"

class ModuleConfig : public Model {
public:
	Prop<std::string> *moduleName;
	ModuleConfig(AbstractPool *_pool,std::string _name,std::string _module):Model(_pool,_name,_module) {
		moduleName = new Prop<std::string>(TEXT,"moduleName",this);
		moduleName->set(_module);
	}
//	ModuleConfig(AbstractPool *_pool,std::string _name):Model(_pool,_name) {
//			moduleName = new Prop<std::string>(TEXT,"moduleName",this);
//
//		}
};

class Module {
protected:
	ModuleConfig *config;
public:
	Module() {
		
	}
	Module (ModuleConfig *_conf) {
		config=_conf;
	}
	void setConfig(ModuleConfig *_conf) {
		config = _conf;
	}
	std::string getModuleName() {
		return config->moduleName->get();
	}
};

template < typename T >
Module* create( void ) {
	return ( new T () );
}

typedef Module* (*CreatePointer)(void);

template < typename T >
Model* createModel( AbstractPool* pool,std::string name,std::string className) {
	return ( new T (pool,name,className) );
}

typedef Model* (*Creator)(AbstractPool*,std::string,std::string);

class IdProvider {
private:
	long id;
public:
	IdProvider() {
		id=0;
	}
	virtual ~IdProvider() {
		
	}
	virtual long getId() {
		return ++id;
	}
};

class DataPool:public AbstractPool {
private:
	IdProvider *idProvider;
	std::map<long,AbstractModel*> *idMap;
	int lock_mutex;
	std::map<std::string,CreatePointer> *creatorMap;
	std::map<std::string,Creator> *modelCreatorMap;
	std::map<std::string,Model*> root;
public:
	DataPool() {
		lock_mutex=0;
		idMap=new std::map<long,AbstractModel*>;
		creatorMap= new std::map<std::string,CreatePointer>;
		modelCreatorMap = new std::map<std::string,Creator>;
		idProvider = new IdProvider();
	}
	~DataPool() {
		delete idMap;
	}
	
	void setIdProvider(IdProvider *prov) {
		idProvider=prov;
	}
	
	IdProvider* getIdProvider() {
		return idProvider;
	}
	
	long getId() {
		return idProvider->getId();
	}
	
	void addRootModel(Model *model) {
		root.insert(std::pair<std::string,Model*>(model->getName(),model));
	}
	
	Model* getRootModel(std::string name) {
		std::map<std::string,Model*>::iterator it;
		it = root.find(name);
		if (it==root.end())
			return 0;
		return it->second;
	}
	
	void registerCreator(std::string _name,CreatePointer p) {
		creatorMap->insert(std::pair<std::string,CreatePointer>(_name,p));
	}
	
	Module* createModule(ModuleConfig *_config){
		std::map<std::string,CreatePointer>::iterator it;
		it = creatorMap->find(_config->moduleName->get());
		
		CreatePointer createPointer = *it->second;
		Module *mod = (*createPointer)();
		mod->setConfig(_config);
		return mod;
	}
	
	void registerModelCreator(std::string _name,Creator p) {
			modelCreatorMap->insert(std::pair<std::string,Creator>(_name,p));
		}
		
		Model* createModel(std::string _className,std::string _name){
			std::map<std::string,Creator>::iterator it;
			it = modelCreatorMap->find(_className);
			
			Creator createPointer = *it->second;
			Model *mod = (*createPointer)(this,_name,_className);
			return mod;
		}
	
	Module* createModule(std::string _name) {
		return createModule((ModuleConfig*)getRootModel(_name));
	}
	
	void registerModel(AbstractModel *_model) {
		long modelid = getId();
		idMap->insert(std::pair<long,AbstractModel*>(modelid,_model));
		_model->setId(modelid);
	}
	
	Model* getById(long _id) {
		return (Model*)idMap->at(_id);
	}
	
	void initTransaction() {
		pthread_t s = pthread_self();
		std::list<ValueContainer*> *list = new std::list<ValueContainer*>;
		transactions->insert(std::pair<pthread_t,std::list<ValueContainer*>*>(s,list));
		inTransaction = IN_TRANSACTION;
	}
	
	
	std::map<pthread_t,std::list<ValueContainer*>* >::iterator getTransactionIterator() {
		pthread_t s = pthread_self();
		std::map<pthread_t,std::list<ValueContainer*>* >::iterator it;
		it = transactions->find(s);
		return it;
	}
	
	std::list<ValueContainer*>* getTransactionList() {
		std::map<pthread_t,std::list<ValueContainer*>* >::iterator it = getTransactionIterator();
		if (it == transactions->end())
			return 0;
		return it->second;
	}

	void registerProperty(ValueContainer *con) {
		
		std::list<ValueContainer*> *list = getTransactionList();
		if (list!=0)
			list->push_back(con);
	}
	
	short hasTransaction() {
			if (inTransaction==NO_TRANSACTION) {
				return NO_TRANSACTION;
			}
			std::map<pthread_t,std::list<ValueContainer*>* >::iterator it = getTransactionIterator();
			if (it != transactions->end())
				return YOUR_TRANSACTION;
			return IN_TRANSACTION;
		}
	
	void closeTransaction() {
		while (lock_mutex>0) {
			usleep(1000);
		}
		pthread_t s = pthread_self();
		std::list<ValueContainer*> *list = getTransactionList();
				if (list!=0) {
					std::list<ValueContainer*>::iterator it;
					for (it = list->begin(); it!=list->end();++it) {
								ValueContainer *con = (ValueContainer*)*it;
								Indexable *in = (Indexable*)con->getPointer();
								in->closeTransaction();
								delete con;
							}
					transactions->erase(s);
					delete list;
				}
		if (transactions->size()==0)
			inTransaction=NO_TRANSACTION;
	}
	
	void lock() {
		lock_mutex++;
	}
	
	void unlock() {
		lock_mutex--;
	}
	
};





class ModelImport {
public:
	static void importModel(DataPool *_pool) {
		Creator modC = &(createModel<Model>);
		Model *mod = (*modC)(_pool,"name","Model");
		std::cout<<mod->getName()<<" \n";

	}
};


class ModulePool : public Model {
public:
	Prop<ModuleConfig*> *newModule;
	
	ModulePool(DataPool *pool,std::string name):Model(pool,name,"ModulePool") {
		newModule = new Prop<ModuleConfig*>(MODEL,"newModule",this);
	}
};

class ModuleListener : public Listener {
public:
	~ModuleListener() {}
	ModuleListener() {
		acceptedValues = MODEL;
	}
	
	short modelChanged(long _id,unsigned int _index,int pointer) {
		Value<Model*> *c = (Value<Model*>* ) pointer;
		ModuleConfig *config = (ModuleConfig*)c->getValue();
//		printf("module added: %s\n",config->moduleName->get());
		DataPool *pool = (DataPool*)config->getPool();
		pool->createModule(config);
		return true;
	}
	
};



#endif /*MODULES_H_*/
